/*
 *    Copyright (c) 2018-2025, cloud All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: cloud
 */
package com.fuyao.cloud.asset.cti.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyao.cloud.asset.constant.CtiConstant;
import com.fuyao.cloud.asset.cti.mapper.AssetCtiMapper;
import com.fuyao.cloud.asset.cti.service.AssetCtiService;
import com.fuyao.cloud.asset.entity.AssetCti;
import com.fuyao.cloud.asset.entity.AssetSau;
import com.fuyao.cloud.asset.sau.mapper.AssetSauMapper;
import com.fuyao.cloud.asset.vo.CtiExcelVO;
import com.fuyao.cloud.asset.vo.CtiImportVO;
import com.fuyao.cloud.common.core.util.DictItemOption;
import com.fuyao.cloud.common.core.util.R;
import com.fuyao.cloud.common.data.datascope.DataScope;
import com.fuyao.cloud.common.data.resolver.DictItemResolver;
import com.fuyao.cloud.common.excel.vo.ErrorMessage;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 资产分类管理
 *
 * @author liyue
 * @date 2021-11-17 17:29:32
 */
@Service
@AllArgsConstructor
public class AssetCtiServiceImpl extends ServiceImpl<AssetCtiMapper, AssetCti> implements AssetCtiService {

	private final AssetSauMapper fyAssetSauMapper;

	@Override
	public boolean save(AssetCti entity) {
		setFieldValue(entity);
		return super.save(entity);
	}

	@Override
	public boolean updateById(AssetCti entity) {
		setFieldValue(entity);
		return super.updateById(entity);
	}

	@Override
	public boolean removeById(Serializable id) {
		Long count = this.baseMapper.selectCount(Wrappers.<AssetCti>query().lambda().eq(AssetCti::getParentId, id));
		Assert.isTrue(count <= 0, "包含子类型，需要删除子类型后在删除父类型！");

		Long countSau = this.fyAssetSauMapper.selectCount(Wrappers.<AssetSau>query().lambda().eq(AssetSau::getCtiId, id));
		Assert.isTrue(countSau <= 0, "此类型在资产型号中有引用，需要删除资产型号中引用类型才可删除！");

		return super.removeById(id);
	}

	@Override
	public List<Tree<Long>> selectTree(QueryWrapper<AssetCti> query) {
		// 查询资产分类信息
		List<AssetCti> ctiList = this.baseMapper.selectList(query);
		// 查询数据权限内资产分类
		List<Long> houseOwnIdList = this.baseMapper.selectListByScope(query, new DataScope()).stream()
				.map(AssetCti::getDeptId).collect(Collectors.toList());
		Map<String, List<DictItemOption>> resultMap = DictItemResolver.getDictOptionResultMap(CtiConstant.DICT_OPTION_TYPES);

		// 权限内房屋信息
		List<TreeNode<Long>> collect = ctiList.stream()
				.sorted(Comparator.comparing(AssetCti::getCreateTime)).map(cti -> {
					TreeNode<Long> treeNode = new TreeNode();
					treeNode.setId(cti.getId());
					treeNode.setParentId(cti.getParentId());
					treeNode.setName(cti.getName());
					treeNode.setWeight(120);
					// 有权限不返回标识
					Map<String, Object> extra = new HashMap<>(8);
					extra.put("isLock", !houseOwnIdList.contains(cti.getDeptId()));
					extra.put("createTime", cti.getCreateTime());
					extra.put("code", cti.getCode());
					extra.put("fullCode", cti.getFullcode());
					extra.put("fullName", cti.getFullname());

					String assetType = DictItemResolver.getDictOptionLabel(resultMap.get(CtiConstant.CTI_ASSET_TYPE), cti.getAssetType());
					String accountingMethod = DictItemResolver.getDictOptionLabel(resultMap.get(CtiConstant.CTI_ACCOUNTING_METHOD), cti.getAccountingMethod());
					extra.put("type", cti.getAssetType());
					extra.put("assetType", assetType);
					extra.put("accountingMethod", accountingMethod);
					treeNode.setExtra(extra);
					return treeNode;
				}).collect(Collectors.toList());
		return TreeUtil.build(collect, 0L);
	}

	@Override
	public List listCti(QueryWrapper<AssetCti> query) {
		// 查询字典信息
		Map<String, List<DictItemOption>> resultMap = DictItemResolver.getDictOptionResultMap(CtiConstant.DICT_OPTION_TYPES);

		List<AssetCti> ctiList = this.baseMapper.selectListByScope(query, new DataScope());
		return ctiList.stream().map(entity -> {
			CtiExcelVO exportVO = new CtiExcelVO();
			BeanUtils.copyProperties(entity, exportVO);
			exportVO.setAssetType(DictItemResolver.getDictOptionLabel(resultMap.get(CtiConstant.CTI_ASSET_TYPE), entity.getAssetType()));
			exportVO.setAccountingMethod(DictItemResolver.getDictOptionLabel(resultMap.get(CtiConstant.CTI_ACCOUNTING_METHOD), entity.getAccountingMethod()));
			return exportVO;
		}).collect(Collectors.toList());
	}

	@Override
	public R importCti(List<CtiImportVO> excelVOList, BindingResult bindingResult) {
		// 通用校验获取失败的数据
		List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
		// 个性化校验逻辑
		Map<String, List<DictItemOption>> resultMap = DictItemResolver.getDictOptionResultMap(CtiConstant.DICT_OPTION_TYPES);

		if (CollUtil.isEmpty(excelVOList)) {
			assert errorMessageList != null;
			errorMessageList.add(new ErrorMessage("无数据导入"));
			return R.failed(errorMessageList);
		}
		// 执行数据插入操作 组装 FyRoomRegional
		for (int i = 0; i < excelVOList.size(); i++) {
			CtiImportVO excel = excelVOList.get(i);
			Set<String> errorMsg = new HashSet<>();

			if (StrUtil.isEmpty(excel.getAssetType())) {
				errorMsg.add(" 资产分类为空， ");
			}
			// 资产分类为易耗品时，需要填写核算分类。
			if (StrUtil.isNotEmpty(excel.getAssetType()) && "易耗品".equals(excel.getAssetType())) {
				if (StrUtil.isEmpty(excel.getAccountingMethod())) {
					errorMsg.add(" 资产分为为易耗品时，核算分类不能为空， ");
				}
			}
			if (StrUtil.isEmpty(excel.getName())) {
				errorMsg.add(" 分类名称为空， ");
			}
			if (StrUtil.isEmpty(excel.getName())) {
				errorMsg.add(" 分类编码为空。 ");
			}
			// 拷贝属性
			AssetCti cti = new AssetCti();
			BeanUtils.copyProperties(excel, cti);

			LambdaQueryWrapper<AssetCti> queryWrapper = Wrappers.<AssetCti>query().lambda().eq(AssetCti::getCode, cti.getCode());
			long count = this.count(queryWrapper);
			if(count > 0){
				errorMsg.add(String.format(" 分类编码%s已存在",cti.getCode()));
			}
			// 名称转字典编码
			cti.setAssetType(DictItemResolver.getDictOptionValue(resultMap.get(CtiConstant.CTI_ASSET_TYPE), excel.getAssetType()));
			cti.setAccountingMethod(DictItemResolver.getDictOptionValue(resultMap.get(CtiConstant.CTI_ACCOUNTING_METHOD), excel.getAccountingMethod()));

			// 根据父级编码处理。
			if (StrUtil.isNotEmpty(excel.getParentCode())) {
				AssetCti assetCti = this.baseMapper.selectOne(Wrappers.<AssetCti>query().lambda().eq(AssetCti::getCode, excel.getParentCode()));
				if (ObjectUtil.isNotEmpty(assetCti)) {
					cti.setParentId(assetCti.getId());
					cti.setParentName(assetCti.getName());
				}
			}

			// 数据合法情况
			if (CollUtil.isEmpty(errorMsg)) {
				this.save(cti);
			} else {
				// 数据不合法情况
				errorMessageList.add(new ErrorMessage((long) (i + 2), errorMsg));
			}
		}
		if (CollUtil.isNotEmpty(errorMessageList)) {
			return R.failed(errorMessageList);
		}
		return R.ok();
	}

	private void setFieldValue(AssetCti cti) {
		// 父级处理
		if (!ObjectUtils.isEmpty(cti.getParentId()) && cti.getParentId() > 0) {
			AssetCti parentCti = this.getById(cti.getParentId());
			cti.setFullcode(parentCti.getFullcode() + "." + cti.getCode());
			cti.setFullname(parentCti.getFullname() + "." + cti.getName());
		} else {
			cti.setFullname(cti.getName());
			cti.setFullcode(cti.getCode());
		}
	}

}
